﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="WindowsHelper.cs" company="TechBits">
//   Copyright (c) TechBits. All rights reserved.
// </copyright>
// <summary>
//   Defines the WindowsHelper type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace IntelliShortCut.Application.Helpers
{
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;

    /// <summary>
    /// WindowsHelper implements managed wrappers for unmanaged Win32 APIs.
    /// </summary>
    public sealed class WindowsHelper
    {
        #region "Constants"

        public const int WM_ACTIVATE = 0x0006;
        public const int WA_CLICKACTIVE = 2;
        public const int SW_HIDE = 0;
        public const int SW_SHOWNORMAL = 1;
        public const int SW_SHOWMINIMIZED = 2;
        public const int SW_SHOWMAXIMIZED = 3;
        public const int SW_SHOWNOACTIVATE = 4;
        public const int SW_SHOW = 5;
        public const int SW_MINIMIZE = 6;
        public const int SW_SHOWMINNOACTIVE = 7;
        public const int SW_SHOWNA = 8;
        public const int SW_RESTORE = 9;
        public const int SW_SHOWDEFAULT = 10;
        public const int SW_FORCEMINIMIZE = 11;
        public const int SW_MAX = 11;
        public const int WPF_RESTORETOMAXIMIZED = 2;

        #endregion

        #region "Public Methods"

        /// <summary>
        /// Finds the specified window by its name (or caption). 
        /// </summary>
        /// <param name="windowName">Name of the window to find and activate.</param>
        /// <returns>Window handle or 0 if window not found by that name.</returns>
        public static uint FindWindow(string windowName)
        {
            // First, try to find the window by its window name or caption.
            return FindWindow(null, windowName);
        }

        /// <summary>
        /// Finds the specified window by its name (or caption).
        /// Then brings it to the foreground.
        /// </summary>
        /// <param name="windowName">Name of the window to find and activate.</param>
        public static void ActivateWindow(string windowName)
        {
            // First, try to find the window by its window name or caption
            var hwndInstance = FindWindow(windowName);

            // Then, get the WindowPlacement, so we can decide the best way to 
            // activate the window correctly
            var placement = new ManagedWindowPlacement();
            GetWindowPlacement(hwndInstance, placement);

            if (placement.showCmd == SW_SHOWMINIMIZED)
            {
                // If the window is minimized, then we need to restore it to its
                // previous size.  We also take into account whether it was 
                // previously maximized.
                var showCmd = (placement.flags == WPF_RESTORETOMAXIMIZED)
                                  ? SW_SHOWMAXIMIZED
                                  : SW_SHOWNORMAL;
                ShowWindow(hwndInstance, showCmd);
            }
            else
            {
                // If it's not minimized, then we just call SetForegroundWindow to 
                // bring it to the front.
                SetForegroundWindow(hwndInstance);
            }
        }

        /// <summary>
        /// Hides a window based on its name.
        /// </summary>
        /// <param name="windowName">The window name.</param>
        public static void HideWindow(string windowName)
        {
            var processRunning = Process.GetProcesses();
            foreach (var pr in processRunning)
            {
                if (!pr.ProcessName.Equals(windowName))
                {
                    continue;
                }

                var wnd = pr.MainWindowHandle.ToInt32();
                ShowWindow((uint) wnd, SW_HIDE);
            }
        }

        /// <summary>
        /// Hides the caret cursor.
        /// </summary>
        /// <param name="handle">The handle.</param>
        /// <returns>
        /// The result of the operation.
        /// </returns>
        public static bool HideCaretCursor(IntPtr handle)
        {
            try
            {
                return HideCaret(handle);
            }
            catch (Exception)
            {
                return false;
            }
        }

        #endregion

        #region "DLL Imports"

        // External Win32 APIs that we're calling directly.
        [DllImport("USER32.DLL", SetLastError = true)]
        private static extern uint ShowWindow(uint hwnd, int showCommand);

        [DllImport("USER32.DLL", SetLastError = true)]
        private static extern uint SetForegroundWindow(uint hwnd);

        [DllImport("USER32.DLL", SetLastError = true)]
        private static extern uint GetWindowPlacement(uint hwnd, [In, Out]ManagedWindowPlacement lpwndpl);

        [DllImport("USER32.DLL", SetLastError = true)]
        private static extern uint FindWindow(string lpClassName, string lpWindowName);

        [DllImport("USER32.DLL", SetLastError = true)]
        private static extern bool ShowCaret(IntPtr hWnd);

        [DllImport("USER32.DLL", SetLastError = true)]
        private static extern bool HideCaret(IntPtr hWnd);

        #endregion

        #region "Structures needed to call into unmanaged Win32 APIs"

        /// <summary>
        /// Point struct used for GetWindowPlacement API.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private class ManagedPt
        {
            /// <summary>
            /// Coordiante X.
            /// </summary>
            public int x;

            /// <summary>
            /// Coordinate Y.
            /// </summary>
            public int y;

            /// <summary>
            /// Initializes a new instance of the <see cref="ManagedPt"/> class.
            /// </summary>
            public ManagedPt()
            {
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="ManagedPt"/> class.
            /// </summary>
            /// <param name="x">The x coordinate.</param>
            /// <param name="y">The y coordinate.</param>
            public ManagedPt(int x, int y)
            {
                this.x = x;
                this.y = y;
            }
        }

        /// <summary>
        /// Rect struct used for GetWindowPlacement API.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private class ManagedRect
        {
            public int x = 0;
            public int y = 0;
            public int right = 0;
            public int bottom = 0;

            public ManagedRect()
            {
            }

            public ManagedRect(int x, int y, int right, int bottom)
            {
                this.x = x;
                this.y = y;
                this.right = right;
                this.bottom = bottom;
            }
        }

        /// <summary>
        /// WindowPlacement struct used for GetWindowPlacement API.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private class ManagedWindowPlacement
        {
            public uint length = 0;
            public uint flags = 0;
            public uint showCmd = 0;
            public ManagedPt minPosition = null;
            public ManagedPt maxPosition = null;
            public ManagedRect normalPosition = null;

            public ManagedWindowPlacement()
            {
                this.length = (uint)Marshal.SizeOf(this);
            }
        }

        #endregion
    }
}